Computer systems often employ object-oriented techniques. An object is composed of instance data (data members) and member functions (method), which implement the behavior of the object. FIG. 1A is a block diagram of typical data structures used to represent an object. The data structures used to represent an object comprise instance data structure 1A01, virtual function table 1A02, and the function members 1A03, 1A04, 1A05. The instance data structure 1A01 contains a pointer to the virtual function table 1A02 and contains data members. The virtual function table 1A02 contains an entry for each virtual function member defined for the object. Each entry contains a reference to the code that implements the corresponding function member. The layout of this sample object conforms to the model defined in U.S. patent application Ser. No. 07/682,537, now U.S. Pat. No. 5,297,294, entitled "METHOD FOR IMPLEMENTING VIRTUAL FUNCTIONS AND VIRTUAL BASES IN A COMPILER FOR AN OBJECT ORIENTED PROGRAMMING LANGUAGE," filed Apr. 9, 1991, which is hereby incorporated by reference. In the following, an object will be described as an instance of a class as defined by the C++ programming language. One skilled in the art would appreciate that objects can be defined using other programming languages.
In the C++ language, object-oriented techniques are supported through the use of classes. A class is a user-defined type. A class declaration describes the data members and function members of the class. For example, the following declaration defines data members and a function member of a class named CIRCLE.
______________________________________ class CIRCLE { public: int x, y; int radius, void draw(); }; ______________________________________
Variables x and y specify the center location of a circle and variable radius specifies the radius of the circle. These variables are referred to as data members of the class CIRCLE. The function draw is a user-defined function that draws the circle of the specified radius at the specified location. The function draw is referred to as a function member of class CIRCLE. The data members and function members of a class are bound together in that the function operates on an instance of the class. An instance of a class is also called an object of the class.
In the syntax of C++, the following statement declares the objects a and b to be of type class CIRCLE.
CIRCLE a, b; PA1 a.x=2; PA1 a.y=2; PA1 a.radius=1; PA1 b.x=4; PA1 b.y=5; PA1 b.radius=2; PA1 class CIRCLE.sub.-- 1: CIRCLE {. . . }; PA1 class CIRCLE.sub.-- 2: CIRCLE {. . . }; PA1 class PATTERN: CIRCLE.sub.-- 1. CIRCLE.sub.-- 2 {. . . }; PA1 class CIRCLE.sub.-- 1: virtual CIRCLE {. . . }; PA1 class CIRCLE.sub.-- 2: virtual CIRCLE {. . . }; PA1 class PATTERN: CIRCLE.sub.-- 1, CIRCLE.sub.-- 2 {. . . }; PA1 CIRCLE *c.sub.-- ptr; PA1 c.sub.-- ptr=&c; PA1 CIRCLE a; PA1 CIRCLE.sub.-- FILL b; PA1 a.draw(); PA1 b.draw(); PA1 CIRCLE *c.sub.-- ptr; PA1 c.sub.-- ptr=&b; PA1 c.sub.-- ptr.fwdarw.draw(); //CIRCLE.sub.-- FILL::draw() PA1 if (pIBasic.fwdarw.Querylnterface("IData", &pIData)==S.sub.-- OK) PA1 * IData supported PA1 else PA1 * IData not supported PA1 static void CreatelnstanceXX (REFIID iid, void **ppv)=0;
This declaration causes the allocation of memory for the objects a and b. The following statements assign data to the data members of objects a and b.
The following statements are used to draw the circles defined by objects a and b.
a.draw();
b.draw();
A derived class is a class that inherits the characteristics-data members and function members--of its base classes. For example, the following derived class CIRCLE.sub.-- FILL inherits the characteristics of the base class CIRCLE.
______________________________________ class CIRCLE.sub.-- FILL : CIRCLE { public: int pattern; void fill(); }; ______________________________________
This declaration specifies that class CIRCLE.sub.-- FILL includes all the data and function members that are in class CIRCLE in addition to those data and function members introduced in the declaration of class CIRCLE.sub.-- FILL, that is, data member pattern and function member fill. In this example, class CIRCLE.sub.-- FILL has data members x, y, radius, and pattern and function members draw and fill. Class CIRCLE FILL is said to "inherit" the characteristics of class CIRCLE. A class that inherits the characteristics of another class is a derived class (e.g., CIRCLE.sub.-- FILL). A class that does not inherit the characteristics of another class is a primary (root) class (e.g., CIRCLE). A class whose characteristics are inherited by another class is a base class (e.g., CIRCLE is a base class of CIRCLE.sub.-- FILL). A derived class may inherit the characteristics of several classes, that is, a derived class may have several base classes. This is referred to as multiple inheritance.
A derived class may specify that a base class is to be inherited virtually. Virtual inheritance of a base class means that only one instance of the virtual base class exists in the derived class. For example, the following is an example of a derived class with two nonvirtual base classes.
In this declaration class PATTERN inherits class CIRCLE twice nonvirtually through classes CIRCLE.sub.-- 1 and CIRCLE.sub.-- 2. There are two instances of class CIRCLE in class PATTERN.
The following is an example of a derived class with two virtual base classes.
The derived class PATTERN inherits class CIRCLE twice virtually through classes CIRCLE.sub.-- 1 and CIRCLE.sub.-- 2. Since the class CIRCLE is virtually inherited twice, there is only one object of class CIRCLE in the derived class PATTERN. One skilled in the art would appreciate virtual inheritance can be very useful when the class derivation is more complex.
A class may also specify whether its function members are virtual. Declaring that a function member is virtual means that the function can be overridden by a function of the same name and type in a derived class. In the following example, the function draw is declared to be virtual in classes CIRCLE and CIRCLE.sub.-- FILL.
______________________________________ class CIRCLE { public: int x, y; int radius; virtual void draw(); }; class CIRCLE.sub.-- FILL : CIRCLE { public: int pattern; virtual void draw(); }; ______________________________________
The C++ language provides a pointer data type. A pointer holds values that are addresses of objects in memory. Through a pointer, an object can be referenced. The following statement declares variable c.sub.-- ptr to be a pointer on an object of type class CIRCLE and sets variable c.sub.-- ptr to hold the address of object c.
Continuing with the example, the following statement declares object a to be of type class CIRCLE and object b to be of type class CIRCLE.sub.-- FILL.
The following statement refers to the function draw as defined in class CIRCLE.
Whereas, the following statement refers to the function draw defined in class CIRCLE.sub.-- FILL.
Moreover, the following statements type cast object b to an object of type class CIRCLE and invoke the function draw that is defined in class CIRCLE.sub.-- FILL.
Thus, the virtual function that is called is function CIRCLE.sub.-- FILL: :draw.
An advantage of using object-oriented techniques is that these techniques can be used to facilitate the sharing of object structures. For example, object-oriented techniques facilitate the creation of compound documents. A compound document is a document that contains objects generated by various computer programs. (Typically, only the data members of the object and the class type are stored in a compound document.) A word processing document that contains a spreadsheet object generated by a spreadsheet program is a compound document. A word processing program allows a user to embed a spreadsheet object (e.g., a cell) within a word processing document. In one way to allow this embedding, the word processing program is typically compiled, using the class definition of the object to be embedded, to access function members of the embedded object. Thus, the word processing program would need to be compiled using the class definition of each class of objects that can be embedded in a word processing document. To embed an object of a new class into a word processing document, the word processing program would need to be recompiled with the new class definition. Thus, only objects of classes selected by the developer of the word processing program can be embedded. Using this technique, new classes can only be supported with a new release of the word processing program.
To allow objects of an arbitrary class to be embedded into compound documents, interfaces are defined through which an object can be accessed without the need for the word processing program to have access to the class implementation at compile time. An abstract class is a class in which a virtual function member has no implementation (pure). An interface is an abstract class with no data members and whose virtual functions are all pure.
The following C++ class definition is an example definition of an interface. In this example, for simplicity of explanation, rather than allowing any class of object to be embedded in its documents, a word processing program allows spreadsheet objects to be embedded. An object that provides an interface is a server object, and an object that uses an interface is a client object. Any spreadsheet object that provides this interface can be embedded, regardless of how the object is implemented. Moreover, any spreadsheet object, whether implemented before or after the word processing program is compiled, can be embedded.
______________________________________ class ISpreadSheet { virtual void File() = 0; virtual void Edit() = 0; virtual void Formula() = 0; virtual void Format() = 0; virtual void GetCell (string RC, cell *pCell) = 0; virtual void Data() = 0; } ______________________________________
The developer of a spreadsheet program would need to provide an implementation of the interface to allow the spreadsheet objects to be embedded in a word processing document. When the word processing program embeds a spreadsheet object, the program needs access to the code that implements the interface for the spreadsheet object. To access the code, each implementation is given a unique class identifier. For example, a spreadsheet object developed by Microsoft Corporation may have a class identifier of "MSSpreadsheet," while a spreadsheet object developed by another corporation may have a class identifier of "LTSSpreadsheet." A persistent registry in each computer system is maintained that maps each class identifier to the code that implements the class. Typically, when a spreadsheet program is installed on a computer system, the persistent registry is updated to reflect the availability of that class of spreadsheet objects. So long as a spreadsheet developer implements every function member defined by the interface and the persistent registry is maintained, the word processing program can embed the developer's spreadsheet objects into a word processing document.
Various spreadsheet developers may wish, however, to implement only certain function members. For example, a spreadsheet developer may not want to implement database support, but may want to support all other function members. To allow a spreadsheet developer to support only some of the function members, while still allowing the objects to be embedded, multiple interfaces for spreadsheet objects are defined. For example, the interfaces IData and IBasicSpreadsheet may be defined for a spreadsheet object as follows.
______________________________________ class IBasicSpreadsheet { virtual void File() = 0; virtual void Edit() = 0; virtual void Formula() = 0; virtual void Format() = 0; virtual void GetCell (string RC, cell *pCell) = 0; } class IData { virtual void Data() = 0; } ______________________________________
Each spreadsheet developer would implement the IBasicSpreadsheet interface and, optionally, the IData interface.
At run time, the word processing program (or any other client of the interface) would need to determine whether a spreadsheet object to be embedded supports the IData interface. To make this determination, another interface is defined (that every spreadsheet object implements) with a function member that indicates which interfaces are implemented for the object. This interface is named IUnknown (and referred to as the unknown interface or the object management interface) and is defined as follows.
______________________________________ class IUnknown { virtual HRESULT QueryInterface (REFIID iid, void **ppv) = 0; virtual ULONG AddRef)() = 0; virtual ULONG Release () = 0; ______________________________________
The IUnknown interface defines the function member (method) QueryInterface. The method QueryInterface is passed an interface identifier (e.g., "IData") in parameter iid (of type REFIID) and returns a pointer to the implementation of the identified interface for the object for which the method is invoked in parameter ppv. If the object does not support the interface, then the method returns a false. (The type HRESULT indicates a predefined status, and the type ULONG indicates an unsigned long integer.)
______________________________________ CODE TABLE 1A ______________________________________ HRESULT XX::Querylnterface(REFIID iid, void **ppv) { ret = TRUE; switch (iid) { case IID.sub.-- IBasicSpreadsheet: *ppv = *pIBasicSpreadsheet; break; case IID.sub.-- IDatabase: *ppv = *pIData; break; case IID.sub.-- IUnknown: *ppv = this; break; default: ret = FALSE; if (ret == TRUE){AddRef();}; return ret; } ______________________________________
Code Table 1A contains C++ pseudocode for a typical implementation of the method QueryInterface for class XX, which inherits the class IUnknown. If the spreadsheet object supports the IData interface, then the method QueryInterface includes the appropriate case label within the switch statement. The variables pIBasicSpreadsheet and pIData point to a pointer to the virtual function tables of the IBasicSpreadsheet and IData interfaces, respectively. The method QueryInterface invokes the method AddRef (described below) to increment a reference count for the object of class XX when a pointer to an interface is returned.
______________________________________ CODE TABLE 1B ______________________________________ void XX::AddRef() {refcount++;} void XX::Release() {if(--refcount==0) delete this;} ______________________________________
The interface IUnknown also defines the methods AddRef and Release, which are used to implement reference counting. Whenever a new reference to an interface is created, the method AddRef is invoked to increment a reference count of the object. Whenever a reference is no longer needed, the method Release is invoked to decrement the reference count of the object and, when the reference count goes to zero, to deallocate the object. Code Table 1B contains C++ pseudocode for a typical implementation of the methods AddRef and Release for class XX, which inherits the class IUnknown.
The IData interface and IBasicSpreadsheet interface inherit the IUnknown interface. The following definitions illustrate the use of the IUnknown interface.
______________________________________ class IData : public IUnknown { public: virtual void Data() = 0:, class IBasicSpreadsheet : public IUnknown { public: virtual void File() = 0; virtual void Edit() = 0; virtual void Formula() = 0; virtual void Format() = 0; virtual void GetCell (string RC, cell *pCell) = 0; } ______________________________________
FIG. 1B is a block diagram illustrating a sample data structure of a spreadsheet object. The spreadsheet object comprises object data structure 1B01, IBasicSpreadsheet interface data structure 1B03, IData interface data structure 1B04, the virtual function tables 1B02, 1B05, 1B06 and methods 1B07 through 1B21. The object data structure 1B01 contains a pointer to the virtual function table 1B02 and pointers to the IBasicSpreadsheet and IData interface. Each entry in the virtual function table 1B02 contains a pointer to a method of the IUnknown interface. The IBasic interface data structure 1B03 contains a pointer to the virtual function table 1B05. Each entry in the virtual function table 1B05 contains a pointer to a method of the IBasicSpreadsheet interface. The IData interface data structure 1B04 contains a pointer to the virtual function table 1B06. Each entry in the virtual function table 1B06 contains a pointer to a method of the IData interface. Since the IBasicSpreadsheet and IData interfaces inherit the IUnknown interface, each virtual function table 1B05 and 1B06 contains a pointer to the methods QueryInterface, AddRef, and Release. In the following, an object data structure is represented by the shape 1B22 labeled with the interfaces through which the object may be accessed.
The following pseudocode illustrates how a word processing program determines whether a spreadsheet object supports the IData interface.
The pointer pIBasicSpreadsheet is a pointer to the IBasicSpreadsheet interface of the object. If the object supports the IData interface, the method QueryInterface sets the pointer pIData to point to the IData data structure and returns the value S.sub.-- OK.
Normally, an object can be instantiated (an instance of the object created in memory) by a variable declaration or by the "new" operator. However, both techniques of instantiation need the class definition at compile time. A different technique is needed to allow a word processing program to instantiate a spreadsheet object at run time. One technique provides a global function CreatelnstanceXX, which is defined in the following.
The method CreatelnstanceXX (known as a class factory) instantiates an object of class XX and returns a pointer ppv to the interface of the object designated by parameter iid.